import React, { useRef, useState, useMemo, forwardRef, useImperativeHandle } from 'react'
import { useObservableState } from 'observable-hooks'
import { map, takeUntil, switchMap } from 'rxjs/operators'
import { fromEvent } from 'rxjs'
import { pick, omit } from 'lodash'

import scss from './styles/progress.module.scss'

const Progress = forwardRef(
  (
    {
      type = 'horizontal',
      width,
      height,
      btnSize = { width: 15, height: 15 },
      progressLen = 0,
      reverse = false,
      customStyle,
    },
    ref
  ) => {
    const wrapRef = useRef()
    const barRef = useRef()

    const [barStyle] = useState({
      width: width ?? '100%',
      height: height ?? '100%',
      ...customStyle?.bar,
    })
    const [isVertical] = useState(type !== 'horizontal')
    const [isMove, setIsMove] = useState(false)
    const [progressStyle, setProgressStyle] = useState({
      width: isVertical ? width : 0,
      height: isVertical ? 0 : height,
      ...customStyle?.progress,
    })
    const [btnStyle, setBtnStyle] = useState({
      ...btnSize,
      left: isVertical ? -(btnSize.width - width) / 2 : 0,
      top: isVertical ? 0 : -(btnSize.height - height) / 2,
    })
    const [pos, setPos] = useState(0)
    const [pct, setPct] = useState(0)
    const [visible, setVisible] = useState(false)

    useMemo(() => {
      if (isVertical && reverse) {
        const top = height - btnSize.height / 2
        setBtnStyle({
          ...pick(btnStyle, ['width', 'height', 'left']),
          top,
        })
      }
    }, [reverse])

    useMemo(() => {
      if (pos) {
        let constant = {}
        let change = {}
        let btnPos = {}
        if (isVertical) {
          constant = omit(progressStyle, ['height', 'top'])
          change = { height: pos, top: height - pos }
          btnPos = { top: height - pos - btnSize.height / 2, ...pick(btnStyle, ['left']) }
          setPct(pos / height)
        } else {
          constant = omit(progressStyle, ['width'])
          change = { width: pos }
          btnPos = { left: pos - btnSize.width / 2, ...pick(btnStyle, ['top']) }
          const w = width ?? barRef.current.clientWidth
          setPct(pos / w)
        }
        setProgressStyle({ ...constant, ...change })
        setBtnStyle({
          ...pick(btnStyle, ['width', 'height']),
          ...btnPos,
        })
      }
    }, [pos])

    useMemo(() => {
      let constant = {
        progress: {},
        btn: {},
      }
      let change = {
        progress: {},
        btn: {},
      }
      if (isVertical) {
        constant.progress = omit(progressStyle, ['height'])
        const top = progressLen !== 0 ? height - progressLen : 0
        change.progress = { height: progressLen, top }
        constant.btn = omit(btnStyle, ['top'])
        change.btn = { top: height - progressLen - btnSize.height / 2 }
      } else {
        constant.progress = omit(progressStyle, ['width'])
        change.progress = { width: progressLen }
        constant.btn = omit(btnStyle, ['left'])
        change.btn = { left: progressLen - btnSize.width / 2 }
      }
      setProgressStyle({
        ...constant.progress,
        ...change.progress,
      })
      setBtnStyle({
        ...constant.btn,
        ...change.btn,
      })
    }, [progressLen])

    useImperativeHandle(ref, () => ({
      pct,
    }))

    useMemo(() => {
      setBtnStyle({
        ...btnStyle,
        display: visible ? 'block' : 'none',
      })
    }, [visible])

    function posRange(pos, client, min) {
      return Math.max(Math.min(pos, client), min)
    }

    function setPosByType({ clientX, clientY }) {
      if (barRef.current) {
        const { left, top, width, height } = barRef.current.getBoundingClientRect()
        const _pos = isVertical ? posRange(height - (clientY - top), height, 0) : posRange(clientX - left, width, 0)
        setPos(_pos)
      }
    }

    // 滑动事件
    const [, onMouseDown] = useObservableState(
      (e$) =>
        e$.pipe(
          map((e$) => {
            // 阻止事件冒泡！！！
            window.event.preventDefault()
            const { clientX, clientY } = e$
            setIsMove(true)
            setPosByType({ clientX, clientY })
          }),
          switchMap(() =>
            fromEvent(window, 'mousemove').pipe(
              map((moveEvent) => {
                const { clientX, clientY } = moveEvent
                setPosByType({ clientX, clientY })
              }),
              takeUntil(
                fromEvent(window, 'mouseup').pipe(
                  map(() => {
                    setIsMove(false)
                  })
                )
              )
            )
          )
        ),
      pos
    )

    return (
      <div
        className={scss.wrap}
        ref={wrapRef}
        onMouseDown={onMouseDown}
        onMouseEnter={() => setVisible(true)}
        onMouseLeave={() => setVisible(false)}
      >
        <div className={scss.btn} style={btnStyle} />
        <div className={scss.progress} style={progressStyle} />
        <div ref={barRef} className={scss.bar} style={barStyle} />
      </div>
    )
  }
)

export default Progress
